﻿using System.Collections.Generic;
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace Ops.Mpc.Collections
{
    /// <summary>
    /// Specialized collection for managing named items.
    /// </summary>
    /// <typeparam name="ItemType">The item type of the generic collection.</typeparam>
    public class NamedItemCollection<ItemType> :  IList, ICollection<ItemType>, INotifyCollectionChanged where ItemType : INamedItem
    {

        #region Fields & Properties
        private Dictionary<string, ItemType> _namedItemDictionary = new Dictionary<string, ItemType>();
        private Collection<ItemType> _namedItems = new Collection<ItemType>();
        public event ResolveItemHandler ResolveItem;
        #endregion



        #region ICollection<ItemType> Members

        /// <summary>
        /// Clears the collection and raises the CollectionChanged event.
        /// </summary>
        public void Clear()
        {
            this._namedItemDictionary.Clear();
            this._namedItems.Clear();
            if (this.CollectionChanged != null)
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        /// <summary>
        /// The number of items in the collection
        /// </summary>
        public int Count
        {
            get
            {
                return this._namedItems.Count;
            }
        }

        /// <summary>
        /// Returns true is the collection is read only
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Adds a new item to the collection.
        /// </summary>
        /// <param name="item">The item to add to the collection</param>
        /// <exception cref="Ops.Mpc.Collections.NamedItemException"/>
        /// <exception cref="System.ArgumentNullException"/>
        public void Add(ItemType item)
        {
            int index = 0;

            if (item == null)
                new ArgumentNullException("item");

            if (string.IsNullOrEmpty(item.ItemName))
                throw new NamedItemException(item.GetType());

            this._namedItemDictionary.Add(item.ItemName, item);
            this._namedItems.Add(item);
            index = this._namedItems.IndexOf(item);

            if (this.CollectionChanged != null)
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,item,index));

        }

        /// <summary>
        /// Evaluates if the item is in the collection by object reference
        /// </summary>
        /// <param name="item">The item to evaluate</param>
        /// <returns>Returns true if the item is in the collection</returns>
        public bool Contains(ItemType item)
        {
            return this._namedItems.Contains(item);
        }

        /// <summary>
        /// Copies the entire Ops.Mpc.Collections.NamedItemCollection<ItemType> collection to an array starting at the start index of the target array
        /// </summary>
        /// <param name="targetArray">The array to copy items to</param>
        /// <param name="startIndex">The starting point in the target array to start copying items</param>
        public void CopyTo(ItemType[] targetArray, int startIndex)
        {
            this._namedItems.CopyTo(targetArray, startIndex);
        }

        /// <summary>
        /// Removes the item from the collection using object reference
        /// </summary>
        /// <param name="item">The item to remove from the collection</param>
        /// <returns>Returns true if the item was removed from the collection</returns>
        public bool Remove(ItemType item)
        {
            int index = this._namedItems.IndexOf(item);
            bool result = this._namedItemDictionary.Remove(item.ItemName);

            if (result && this.CollectionChanged != null)
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item, index));

            return result;
        }

        #endregion

        #region IEnumerable<INamedItem> Members
   

        /// <summary>
        /// Returns an IEnumerator to enumerate over the collection.
        /// </summary>
        /// <returns>Returns an IEnumerator</returns>
        IEnumerator<ItemType> IEnumerable<ItemType>.GetEnumerator()
        {
            return this._namedItems.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an IEnumerator to enumerate over the collection.
        /// </summary>
        /// <returns>Returns an IEnumerator</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this._namedItems.GetEnumerator();
        }

        #endregion



        #region IList Members

        public int Add(object value)
        {
            ItemType itemType = (ItemType) value;
            this.Add(itemType);
            return this._namedItems.IndexOf(itemType);
        }

        public bool Contains(object value)
        {
            ItemType itemType = (ItemType)value;
            return this._namedItems.Contains(itemType);
        }

        public int IndexOf(object value)
        {
            ItemType itemType = (ItemType)value;
            return this._namedItems.IndexOf(itemType);
        }

        public void Insert(int index, object value)
        {
            ItemType itemType = (ItemType)value;
            this._namedItemDictionary.Add(itemType.ItemName, itemType);
            this._namedItems.Insert(index, itemType);
            
            if (this.CollectionChanged != null)
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value, index));


        }

        public bool IsFixedSize
        {
            get { return false; }
        }

        public void Remove(object value)
        {
            ItemType itemType = (ItemType)value;
            this.Remove(itemType);
        }

        public void RemoveAt(int index)
        {
            ItemType itemType = this._namedItems[index];
            this.Remove(itemType);
        }

        object IList.this[int index]
        {
            get
            {
                return this._namedItems[index];
            }
            set
            {
                this.Insert(index, value);
            }

        }

        public ItemType this[int index]
        {
            get
            {
                return this._namedItems[index];
            }
            set
            {
                this.Insert(index, value);
            }
        }

        public ItemType this[string itemName]
        {
            get
            {
                if (!this._namedItemDictionary.ContainsKey(itemName))
                {
                    if (this.ResolveItem != null)
                        return (ItemType) this.ResolveItem(itemName);
                    else
                        return default(ItemType);
                }
                else
                    return this._namedItemDictionary[itemName];
            }
        }

        #endregion

        #region ICollection Members

        public void CopyTo(Array array, int index)
        {
            ((ICollection)this._namedItems).CopyTo(array, index);
        }

        public bool IsSynchronized
        {
            get { return ((ICollection)this._namedItems).IsSynchronized; }
        }

        public object SyncRoot
        {
            get { return ((ICollection)this._namedItems).SyncRoot; }
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion
    }
}
